﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Finance.Controllers;
using Finance.Data.Entities;
using Finance.TodoSoft;

namespace Finance.ViewModels
{
	internal class RepayDebtDocumentViewModel : DocumentViewModelBase
	{
		private IRepayDebtDocumentController _controller;
		private string _debtAmount;
		public Guid DebtDocumentId { get; set; }
		private bool _autocalculateDebtAmount = true;
		private bool _autocalculateRateAmount = true;
		private string _rateAmount;
		private string _debtRemain;
		private decimal _initialRemainDebtAmount;
		private NamedListItem<IDebtDocument> _selectedDebtDocument;

		public RepayDebtDocumentViewModel(
			IRepayDebtDocument document, 
			IRepayDebtDocumentController  controller)
			: base(document, controller)
		{
			_controller = controller;
			DebtDocumentId = document.DebtDocumentId;

			var debtDocumentType = document.DocumentType == DocumentType.DohodZaimBack
				                       ? DocumentType.RashodZaim
				                       : DocumentType.DohodZaim;

			DebtDocuments = new List<NamedListItem<IDebtDocument>> {
					new NamedListItem<IDebtDocument> {Tag = null, Name = ""}
				};
			foreach (var d in _controller.RetrieveDebtDocuments(debtDocumentType))
			{
				DebtDocuments.Add(new NamedListItem<IDebtDocument> { 
					Name = d.DocDate.ToShortDateString() + ":" + d.Amount + 
					(d.Contractor != null ? " - " + d.ContractorName : ""), Tag = d });
			}
				
			_autocalculateDebtAmount = false;

			SelectedDebtDocument = Guid.Empty.Equals(DebtDocumentId) ? DebtDocuments[0] : DebtDocuments.First(d => d.Tag != null && d.Tag.Id.Equals(DebtDocumentId));

			DecimalDebtAmount = document.DebtAmount;
			UpdateInitialDebt();

			if (document.RateAmount != 0m || SelectedDebtDocument.Tag == null)
				DecimalRateAmount = document.RateAmount;
			else
				DecimalRateAmount = _controller.GetRemainRate(SelectedDebtDocument.Tag, DocDate, InitialRemainDebtAmount);
			
			_autocalculateDebtAmount = true;
			_autocalculateRateAmount = document.RateAmount == 0m;

			PropertyChanged += OnPropertyChanged;
		}

		private void UpdateInitialDebt()
		{
			if (SelectedDebtDocument.Tag != null)
			{
				InitialRemainDebtAmount = _controller.GetDebtRemain(SelectedDebtDocument.Tag, DocDate);
			}
			else
			{
				DebtRemain = "";
			}
		}

		void OnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
		{
			switch (e.PropertyName)
			{
				case "DocDate":
					UpdateInitialDebt();
					if (_autocalculateRateAmount)
						SetRateAmount(SelectedDebtDocument.Tag == null ? "0.00" :
							_controller.GetRemainRate(SelectedDebtDocument.Tag, DocDate, InitialRemainDebtAmount)
							.ToString("F", CultureInfo.InvariantCulture));
					break;
				case "SelectedDebtDocument":
					UpdateInitialDebt();
					if(_autocalculateRateAmount)
						SetRateAmount(SelectedDebtDocument.Tag == null ? "0.00" :
							_controller.GetRemainRate(SelectedDebtDocument.Tag, DocDate, InitialRemainDebtAmount)
							.ToString("F", CultureInfo.InvariantCulture));
					if (SelectedDebtDocument.Tag != null)
					{
						ContractorName = SelectedDebtDocument.Tag.Contractor != null 
							? SelectedDebtDocument.Tag.Contractor.Name 
							: string.Empty;
						SubTypeName = SelectedDebtDocument.Tag.SubTypeName;
					}
					break;
			}
		}

		protected decimal InitialRemainDebtAmount
		{
			get { return _initialRemainDebtAmount; }
			set
			{
				_initialRemainDebtAmount = value;
				DecimalRemainDebtAmount = _initialRemainDebtAmount - DecimalDebtAmount;
			}
		}

		public override void ApplyChangesToModel()
		{
			base.ApplyChangesToModel();

	        var repayDebtDocument = ((IRepayDebtDocument) Document);

			repayDebtDocument.DebtDocumentId = 
				SelectedDebtDocument.Tag != null 
				? SelectedDebtDocument.Tag.Id 
				: Guid.Empty;

			repayDebtDocument.DebtAmount = DecimalDebtAmount;
			repayDebtDocument.RateAmount = DecimalRateAmount;
		}

		public IList<NamedListItem<IDebtDocument>> DebtDocuments { get; private set; }
		public NamedListItem<IDebtDocument> SelectedDebtDocument
		{
			get { return _selectedDebtDocument; }
			set
			{
				if (Equals(value, _selectedDebtDocument)) return;
				_selectedDebtDocument = value;
				RaisePropertyChanged("SelectedDebtDocument");
			}
		}

		public string RateAmount
		{
			get { return _rateAmount; }
			set
			{
				if (value == _rateAmount) return;
				_autocalculateRateAmount = value == "";

				SetRateAmount(value);
			}
		}

		private void SetRateAmount(string value)
		{
			_rateAmount = value;
			if (_autocalculateDebtAmount && Amount != "")
				SetDebtAmount((_decimalAmount - DecimalRateAmount).ToString("F", CultureInfo.InvariantCulture));
			RaisePropertyChanged("RateAmount");
		}

		public string DebtAmount
		{
			get { return _debtAmount; }
			set
			{
				if (value == _debtAmount) return;
				_autocalculateDebtAmount = value == "";

				SetDebtAmount(value);
			}
		}

		private void SetDebtAmount(string value)
		{
			_debtAmount = value;
			UpdateRemainDebtAmount();
			RaisePropertyChanged("DebtAmount");
		}

		public new string Amount 
		{ 
			get
			{
				return base.Amount;
			} 
			set
			{
				base.Amount = value;

				if (_autocalculateDebtAmount)
					DecimalDebtAmount = _decimalAmount - DecimalRateAmount;
			} 
		}

		private void UpdateRemainDebtAmount()
		{
			if (SelectedDebtDocument.Tag == null)
				DebtRemain = "";
			else
				DecimalRemainDebtAmount = InitialRemainDebtAmount - DecimalDebtAmount;
		}

		protected decimal DecimalDebtAmount
		{
			set
			{
				SetDebtAmount(value.ToString("F", CultureInfo.InvariantCulture));
			}
			get
			{
				try
				{
					return DebtAmount == null ? 0m : DecimalExtension.ParseCurrency(DebtAmount);
				}
				catch (FormatException)
				{
					return 0m;
				}
			}
		}

		protected decimal DecimalRateAmount
		{
			set
			{
				RateAmount = value.ToString("F", CultureInfo.InvariantCulture);
			}
			get
			{
				try
				{
					return DecimalExtension.ParseCurrency(RateAmount);
				}
				catch (FormatException)
				{
					return 0m;
				}
			}
		}

		protected decimal DecimalRemainDebtAmount 
		{
			set
			{
				DebtRemain = value.ToString("F", CultureInfo.InvariantCulture);
			} 
		}

		public string DebtRemain
		{
			get { return _debtRemain; }
			private set
			{
				if (value == _debtRemain) return;
				_debtRemain = value;
				RaisePropertyChanged("DebtRemain");
			}
		}
	}

	internal class NamedListItem<T>
	{
		public string Name;
		public T Tag;
		public override string ToString()
		{
			return Name;
		}
	}
}
